// PanelOperations.cpp

#include "StdAfx.h"

#include "Directory.h"

#include "Common/String/StringConvert.h"

#include "Common/FileSystem/FileDir.h"
#include "Common/MS/PropVariant.h"
#include "Common/String/ResourceString.h"

#include "../../ComboDialog.h"

#include "Common/FileSystem/FSFolder.h"
#include "Common/String/FormatUtils.h"
#include "Common/Lang/LangUtils.h"
#include "UpdateCallback100.h"

#include "../../resource.h"

#include "../../App.h"
#include "../Windows.h"


namespace FileManager
{
namespace FileSystem
{

using namespace Common;

enum class FolderOpType
{
	FOLDER_TYPE_CREATE_FOLDER = 0,
	FOLDER_TYPE_DELETE = 1,
	FOLDER_TYPE_RENAME = 2
};

class CThreadFolderOperations: public CProgressThreadVirt
{
	HRESULT ProcessVirt();
public:
	FolderOpType OpType;
	CString Name;
	UINT32 Index;
	std::vector<UINT32> Indices;

	CComPtr<IFolderOperations> FolderOperations;
	CComPtr<IProgress> UpdateCallback;
	CUpdateCallback100Imp* UpdateCallbackSpec;

	HRESULT Result;

	CThreadFolderOperations(FolderOpType opType, CProgressThreadVirt::ThreadPoolType* pool):
		OpType(opType), Result(E_FAIL), CProgressThreadVirt(pool) {}
	HRESULT DoOperation(CDirectory &directory, const CString &progressTitle, const CString &titleError);
};

HRESULT CThreadFolderOperations::ProcessVirt()
{
	switch(OpType)
	{
		case FolderOpType::FOLDER_TYPE_CREATE_FOLDER:
			Result = FolderOperations->CreateFolder(Name, UpdateCallback);
			break;
		case FolderOpType::FOLDER_TYPE_DELETE:
			Result = FolderOperations->Delete(&Indices.front(), Indices.size(), UpdateCallback);
			break;
		case FolderOpType::FOLDER_TYPE_RENAME:
			Result = FolderOperations->Rename(Index, Name, UpdateCallback);
			break;
		default:
			Result = E_FAIL;
	}
	return Result;
}


HRESULT CThreadFolderOperations::DoOperation(CDirectory &directory, const CString &progressTitle, const CString &titleError)
{
	UpdateCallbackSpec = new CComObject<CUpdateCallback100Imp>;
	UpdateCallbackSpec->FinalConstruct();
	UpdateCallback = UpdateCallbackSpec;
	UpdateCallbackSpec->ProgressDialog = &ProgressDialog;

	ProgressDialog.WaitMode = true;
	ProgressDialog.Sync.SetErrorMessageTitle(titleError);
	Result = S_OK;

	bool usePassword = false;
	CString password;
	if (directory._parentFolders.size() > 0)
	{
		const auto &fl = directory._parentFolders.back();
		usePassword = fl->UsePassword;
		password = fl->Password;
	}

	UpdateCallbackSpec->Init(usePassword, password);

	ProgressDialog.MainWindow = directory.m_hWnd; // panel.GetParent()
	ProgressDialog.MainTitle = Lang::LangString(IDS_APP_TITLE, 0x03000000);
	ProgressDialog.MainAddTitle = progressTitle + CString(_T(" "));

	HRESULT hRes(Create(progressTitle, ProgressDialog.MainWindow));
	if(S_OK != hRes)
	{
		return hRes;
	}
	return Result;
}

void CDirectory::DeleteItems(std::vector<UINT32> &indices, bool toRecycleBin)
{
	assert(0 < indices.size());
	CDirectory::CDisableTimerProcessing disableTimerProcessing2(*this);
	
#ifndef UNDER_CE
	// WM6 / SHFileOperationW doesn't ask user! So we use internal delete
	bool useInternalDelete = false;
	if (IsFSFolder() && toRecycleBin)
	{
		CString strFinalPath;
		TCHAR* pBuffer = NULL;
		size_t size = 0;
		int maxLen = 0;
		for (size_t i = 0; i < indices.size(); i++)
		{
			// _T("\\\\?\\")) doesn't work here. See SHFileOperation documentation
			const CString path = GetFsPath() + GetItemRelPath(indices[i]);
			if (path.GetLength() > maxLen)
			{
				maxLen = path.GetLength();
			}
			pBuffer = strFinalPath.GetBufferSetLength(size + path.GetLength() + 1);
			pBuffer += size;
			_tcscpy(pBuffer, path);
			strFinalPath.ReleaseBuffer();

			size += path.GetLength() + 1;
		}
		pBuffer = strFinalPath.GetBufferSetLength(size + 1);
		pBuffer[size] = 0;
		SHFILEOPSTRUCT fo;
		fo.hwnd = m_hWnd;
		fo.wFunc = FO_DELETE;
		fo.pFrom = (LPCTSTR)pBuffer;
		fo.pTo = 0;
		fo.fFlags = 0;
		if (toRecycleBin)
		{
			fo.fFlags |= FOF_ALLOWUNDO;
		}
		fo.fAnyOperationsAborted = FALSE;
		fo.hNameMappings = 0;
		fo.lpszProgressTitle = 0;
		int res;
		res = ::SHFileOperation(&fo);
		/*
		if (fo.fAnyOperationsAborted)
		  MessageBoxError(result, LangString(IDS_ERROR_DELETING, 0x03020217));
		*/
	}
	else
	{
		useInternalDelete = true;
	}
	if (useInternalDelete)
#endif
		DeleteItemsInternal(indices);
}


void CDirectory::DeleteItemsInternal(std::vector<UINT32> &indices)
{
	CComPtr<IFolderOperations> folderOperations;
	if (_folder.QueryInterface(&folderOperations) != S_OK)
	{
		CWindows::MessageBoxErrorForUpdate(m_hWnd, E_NOINTERFACE, IDS_ERROR_DELETING, 0x03020217);
		return;
	}

	CString title;
	CString message;
	if (indices.size() == 1)
	{
		int index = indices[0];
		const CString itemName = GetItemRelPath(index);
		if (IsItemFolder(index))
		{
			title = Lang::LangString(IDS_CONFIRM_FOLDER_DELETE, 0x03020211);
			message = String::PatternFormat(IDS_WANT_TO_DELETE_FOLDER, 0x03020214, itemName);
		}
		else
		{
			title = Lang::LangString(IDS_CONFIRM_FILE_DELETE, 0x03020210);
			message = String::PatternFormat(IDS_WANT_TO_DELETE_FILE, 0x03020213, itemName);
		}
	}
	else
	{
		title = Lang::LangString(IDS_CONFIRM_ITEMS_DELETE, 0x03020212);
		message = String::PatternFormat(IDS_WANT_TO_DELETE_ITEMS, 0x03020215,
										String::NumberToString(indices.size()));
	}
	if (::MessageBox(m_hWnd, message, title, MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
	{
		return;
	}

	{
		CThreadFolderOperations op(FolderOpType::FOLDER_TYPE_DELETE, &m_pool);
		op.FolderOperations = folderOperations;
		op.Indices.assign(indices.begin(), indices.end());
		op.DoOperation(*this,
					   Lang::LangString(IDS_DELETING, 0x03020216),
					   Lang::LangString(IDS_ERROR_DELETING, 0x03020217));
	}
	
}

bool CDirectory::EditName(int nIndex, const CString & newName)
{
	assert(!newName.IsEmpty());
	//assert(kParentIndex != nIndex);
	CComPtr<IFolderOperations> folderOperations;
	if (_folder.QueryInterface(&folderOperations) != S_OK)
	{
		CWindows::MessageBoxErrorForUpdate(m_hWnd, E_NOINTERFACE, IDS_ERROR_RENAMING, 0x03020221);
		return false;
	}
	CDisableTimerProcessing disableTimerProcessing2(*this);

	if (kParentIndex == nIndex)
	{
		return false;
	}

	{
		CThreadFolderOperations op(FolderOpType::FOLDER_TYPE_RENAME, &m_pool);
		op.FolderOperations = folderOperations;
		op.Index = nIndex;
		op.Name = newName;
		HRESULT res = op.DoOperation(*this,
									 Lang::LangString(IDS_RENAMING, 0x03020220),
									 Lang::LangString(IDS_ERROR_RENAMING, 0x03020221));
		if (res != S_OK)
		{
			return false;
		}
	}

	return true;
}

bool CDirectory::CreateFolder(CString & name)
{
	name.Empty();
	CComPtr<IFolderOperations> folderOperations;
	if (_folder.QueryInterface(&folderOperations) != S_OK)
	{
		CWindows::MessageBoxErrorForUpdate(m_hWnd, E_NOINTERFACE, IDS_CREATE_FOLDER_ERROR, 0x03020233);
		return false;
	}
	CDirectory::CDisableTimerProcessing disableTimerProcessing2(*this);
	CComboDialog comboDialog;
	comboDialog.Title = Lang::LangString(IDS_CREATE_FOLDER, 0x03020230);
	comboDialog.Static = Lang::LangString(IDS_CREATE_FOLDER_NAME, 0x03020231);
	comboDialog.Value = Lang::LangString(IDS_CREATE_FOLDER_DEFAULT_NAME, /*0x03020232*/ (UINT32) - 1);
	if (comboDialog.DoModal() == IDCANCEL)
	{
		return false;
	}

	CString newName = comboDialog.Value;

	{
		CThreadFolderOperations op(FolderOpType::FOLDER_TYPE_CREATE_FOLDER, g_App.GetUIThreadPool());
		op.FolderOperations = folderOperations;
		op.Name = newName;
		HRESULT res = op.DoOperation(*this,
									 Lang::LangString(IDS_CREATE_FOLDER, 0x03020230),
									 Lang::LangString(IDS_CREATE_FOLDER_ERROR, 0x03020233));
		if (res != S_OK)
		{
			return false;
		}
	}
	int pos = newName.Find(CHAR_PATH_SEPARATOR);
	if (pos >= 0)
	{
		name = newName.Left(pos);
	}
	return true;
}

bool CDirectory::CreateFile(CString & name)
{
	name.Empty();
	CComPtr<IFolderOperations> folderOperations;
	if (_folder.QueryInterface(&folderOperations) != S_OK)
	{
		CWindows::MessageBoxErrorForUpdate(m_hWnd, E_NOINTERFACE, IDS_CREATE_FILE_ERROR, 0x03020243);
		return false;
	}
	CDirectory::CDisableTimerProcessing disableTimerProcessing2(*this);
	CComboDialog comboDialog;
	comboDialog.Title = Lang::LangString(IDS_CREATE_FILE, 0x03020240);
	comboDialog.Static = Lang::LangString(IDS_CREATE_FILE_NAME, 0x03020241);
	comboDialog.Value = Lang::LangString(IDS_CREATE_FILE_DEFAULT_NAME, /*0x03020242*/ (UINT32) - 1);
	if (comboDialog.DoModal() == IDCANCEL)
	{
		return false;
	}
	CString newName = comboDialog.Value;
	HRESULT result = folderOperations->CreateFile(newName, 0);
	if (result != S_OK)
	{
		CWindows::MessageBoxErrorForUpdate(m_hWnd, result, IDS_CREATE_FILE_ERROR, 0x03020243);
		return false ;
	}
	int pos = newName.Find(CHAR_PATH_SEPARATOR);
	if (pos >= 0)
	{
		name = newName.Left(pos);
	}
	return true;
}



bool CDirectory::ChangeComment(int nIndex)
{
	if (kParentIndex == nIndex)
	{
		return false;
	}
	CComPtr<IFolderOperations> folderOperations;
	if (_folder.QueryInterface(&folderOperations) != S_OK)
	{
		CWindows::MessageBoxErrorLang(m_hWnd, IDS_OPERATION_IS_NOT_SUPPORTED, 0x03020208);
		return false;
	}

	CString comment;
	{
		CComPropVariant propVariant;
		if (_folder->GetProperty(nIndex, kpidComment, &propVariant) != S_OK)
		{
			return false;
		}
		if (propVariant.vt == VT_BSTR)
		{
			comment = propVariant.bstrVal;
		}
		else if (propVariant.vt != VT_EMPTY)
		{
			return false;
		}
	}
	CString name = GetItemRelPath(nIndex);
	CComboDialog comboDialog;
	comboDialog.Title = name + _T(" ") + Lang::LangString(IDS_COMMENT, 0x03020290);
	comboDialog.Value = comment;
	comboDialog.Static = Lang::LangString(IDS_COMMENT2, 0x03020291);
	if (comboDialog.DoModal() == IDCANCEL)
	{
		return false;
	}
	CComPropVariant propVariant = CComBSTR(comboDialog.Value);

	HRESULT result = folderOperations->SetProperty(nIndex, kpidComment, &propVariant, NULL);
	if (result != S_OK)
	{
		if (result == E_NOINTERFACE)
		{
			CWindows::MessageBoxErrorLang(m_hWnd, IDS_OPERATION_IS_NOT_SUPPORTED, 0x03020208);
		}
		else
		{
			CWindows::MessageBoxError(m_hWnd, result, _T("Set Comment Error"));
		}
	}
	return true;
}

} //namespace FileSystem
} //namespace FileManager